package com.smaato.comms;

import java.io.IOException;
import java.io.InputStream;

/**
 * When reading from the network, if you try to read more data than is available
 * from an input stream, it will block the thread until the TCP timeout is hit.
 * This class wraps any InputStream to prevent that from happening.
 */
public class SafeInputStream extends InputStream {

	private InputStream _inputStream;
	private int _totalBytesRead = 0;

	public SafeInputStream(InputStream inputStream) {
		_inputStream = inputStream;
	}

	/**
	 * @see java.io.InputStream#available()
	 */
	public int available() throws IOException {
		return _inputStream.available();
	}

	/**
	 * @see java.io.InputStream#close()
	 */
	public void close() throws IOException {
		_inputStream.close();
	}

	/**
	 * @see java.io.InputStream#mark(int)
	 */
	public synchronized void mark(int readlimit) {
		_inputStream.mark(readlimit);
	}

	/**
	 * @see java.io.InputStream#markSupported()
	 */
	public boolean markSupported() {
		return _inputStream.markSupported();
	}

	/**
	 * @see java.io.InputStream#read()
	 */
	public int read() throws IOException {
		return _inputStream.read();
	}

	/**
	 * @see java.io.InputStream#read(byte[], int, int)
	 */
	public int read(byte[] b, int off, int len) throws IOException {
		int bytesRead = 0;
		int bytesAvailable = available();
		if (bytesAvailable > 0) {
			// ensure that we never try to read more than is available
			int numBytesToRead = Math.min(bytesAvailable, len);
			bytesRead = _inputStream.read(b, off, numBytesToRead);
		}
		else {
			// check if no data is available due to EOF
			int data = _inputStream.read();
			if (data == -1) {
				// EOF reached
				bytesRead = -1;
			}
			else {
				bytesRead = 1;
				b[off] = (byte) data;
			}
		}
		_totalBytesRead += bytesRead;
		return bytesRead;
	}

	/**
	 * @see java.io.InputStream#read(byte[])
	 */
	public int read(byte[] b) throws IOException {
		return read(b, 0, b.length);
	}

	/**
	 * @see java.io.InputStream#reset()
	 */
	public synchronized void reset() throws IOException {
		_inputStream.reset();
	}

	/**
	 * @see java.io.InputStream#skip(long)
	 */
	public long skip(long n) throws IOException {
		long bytesToSkip = Math.min(available(), n);
		return _inputStream.skip(bytesToSkip);
	}

}